#include "Effect.h"
#include "D3d11Utils.h"
#include "D3D11.h"
#include "D3DX11tex.h"

Effect::Effect()
{
	
	mpVertexByteCode = NULL;
	mpVertexShader = NULL;
	mpPixelShader = NULL;
	mpGeometryShader = NULL;
	mpHullShader = NULL;
	mpDomainShader = NULL;
}

Effect* Effect::Create(DeviceManager* pDeviceManager, InputLayoutDesc* pLayoutDesc, char* pFilename, char* pVertexShaderEntry, char* pPixelShaderEntry, char* pGeometryShaderEntry, char* pDomainShaderEntry, char* pHullShaderEntry, bool model5)
{
	HRESULT hr = S_OK;

	Effect* effect = new Effect();

	effect->mpDeviceManager = pDeviceManager;
	
	if(pVertexShaderEntry != NULL)
	{
		if(!effect->Compile(pFilename, pVertexShaderEntry, Target_VertexShader, model5))
		{
			effect->Release();
			delete effect;
			return NULL;
		}
	}

	if(pPixelShaderEntry != NULL)
	{
		if(!effect->Compile(pFilename, pPixelShaderEntry, Target_PixelShader, model5))
		{
			effect->Release();
			delete effect;
			return NULL;
		}
	}

	if(pGeometryShaderEntry != NULL)
	{
		if(!effect->Compile(pFilename, pGeometryShaderEntry, Target_GeometryShader, model5))
		{
			effect->Release();
			delete effect;
			return NULL;
		}
	}

	if( pDomainShaderEntry != NULL)
	{
		if(!effect->Compile(pFilename, pDomainShaderEntry, Target_DomainShader, model5))
		{
			effect->Release();
			delete effect;
			return NULL;
		}
	}

	if(pHullShaderEntry != NULL)
	{
		if(!effect->Compile(pFilename, pHullShaderEntry, Target_HullShader, model5))
		{
			effect->Release();
			delete effect;
			return NULL;
		}
	}

	HR(hr = pDeviceManager->mpDevice->CreateInputLayout( pLayoutDesc->mpDesc, pLayoutDesc->mDescSize, effect->mpVertexByteCode, effect->mVertexByteSize, &(effect->mpInputLayout) ));
	if(hr!=S_OK)
	{
		effect->Release();
		delete effect;
		return NULL;
	}	


	return effect;
}

bool Effect::Compile(char* pFilename, char* pEntryPoint, ShaderTarget target, bool model5)
{
	char* model;

	switch(target)
	{
	case Target_VertexShader:
		if(model5)
			model="vs_5_0";
		else
			model="vs_4_0";
		break;

	case Target_PixelShader:
		if(model5)
			model="ps_5_0";
		else
			model="ps_4_0";
		break;

	case Target_GeometryShader:
		if(model5)
			model="gs_5_0";
		else
			model="gs_4_0";	
		break;
		
	case Target_HullShader:
		if(model5)
			model="hs_5_0";
		else
			model="hs_4_0";
		break;
		
	case Target_DomainShader:
		if(model5)
			model="ds_5_0";
		else
			model="ds_4_0";
		break;
	}

	ID3DBlob* code;
	ID3DBlob* error;
	
	HR(D3DX11CompileFromFileA( pFilename, NULL, NULL, pEntryPoint, model, 0, 0, NULL, &code, &error,NULL));

	switch(target)
	{
	case Target_VertexShader:
		mpVertexByteCode = new byte[code->GetBufferSize()];
		mVertexByteSize = code->GetBufferSize();
		memcpy_s(mpVertexByteCode, code->GetBufferSize(), code->GetBufferPointer(), code->GetBufferSize());
		HR(mpDeviceManager->mpDevice->CreateVertexShader(code->GetBufferPointer(), code->GetBufferSize(), NULL, &mpVertexShader) );
		break;

	case Target_PixelShader:
		HR(mpDeviceManager->mpDevice->CreatePixelShader(code->GetBufferPointer(), code->GetBufferSize(), NULL, &mpPixelShader) );
		break;

	case Target_GeometryShader:
		HR (mpDeviceManager->mpDevice->CreateGeometryShader(code->GetBufferPointer(), code->GetBufferSize(), NULL, &mpGeometryShader) );
		break;
		
	case Target_HullShader:
		HR (mpDeviceManager->mpDevice->CreateHullShader(code->GetBufferPointer(), code->GetBufferSize(), NULL, &mpHullShader) );
		break;
		
	case Target_DomainShader:
		HR (mpDeviceManager->mpDevice->CreateDomainShader(code->GetBufferPointer(), code->GetBufferSize(), NULL ,&mpDomainShader) );
		break;
	}

	SAFE_RELEASE(code);
	SAFE_RELEASE(error);

	return true;
}

ID3D11InputLayout* Effect::GetInputLayout() const
{
	return mpInputLayout;
}

LPVOID Effect::GetByteCodeBufferPointer() const
{
	return mpVertexByteCode;
}

int Effect::GetByteCodeBufferSize() const
{
	return mVertexByteSize;
}

void Effect::Release()
{
	if( mpVertexByteCode != NULL)
	{
		delete[] mpVertexByteCode;
	}

	SAFE_RELEASE(mpVertexShader);
	SAFE_RELEASE(mpPixelShader);
	SAFE_RELEASE(mpGeometryShader);
	SAFE_RELEASE(mpHullShader);
	SAFE_RELEASE(mpDomainShader);

	SAFE_RELEASE(mpInputLayout);
}

void Effect::Apply()
{
	mpDeviceManager->mpContext->VSSetShader(mpVertexShader, NULL, 0);
	mpDeviceManager->mpContext->PSSetShader(mpPixelShader, NULL, 0);
	mpDeviceManager->mpContext->GSSetShader(mpGeometryShader, NULL, 0);
	mpDeviceManager->mpContext->HSSetShader(mpHullShader, NULL, 0);
	mpDeviceManager->mpContext->DSSetShader(mpDomainShader, NULL, 0);
}